package com.zq.sdklib.transform.cost.asm

import org.objectweb.asm.*
import org.objectweb.asm.commons.AdviceAdapter

class AsmMethodCostMethodVisitor(
    api: Int,
    methodVisitor: MethodVisitor?,
    access: Int,
    name: String?,
    descriptor: String?,
) : AdviceAdapter(api, methodVisitor, access, name, descriptor) {

    var owner = ""
    var hasMethodCost = false
    var labelBegin :Label? = null
    var labelEnd :Label? = null
    var time :Int? = null
    init {
        owner = name?:""
        val args = Type.getArgumentTypes(descriptor)
        for (type in args) {
            println("参数 name = " + type.internalName.toString() + " , classname = " + type.className)
        }
    }

    override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor {
        println("$owner visitAnnotation: descriptor:$descriptor visible:$visible")
        if("Lcom/zq/cost/MethodCost;".equals(descriptor)){
            hasMethodCost = true
        }
        return super.visitAnnotation(descriptor, visible)
    }

    //开始扫描该方法时回调该方法
    override fun visitCode() {
        println("$owner visitCode:")
        super.visitCode()
    }

    override fun visitParameter(name: String?, access: Int) {
        println("$owner visitParameter: access:$access name:$name")
        super.visitParameter(name, access)
    }

    //进入这个方法
    override fun onMethodEnter() {
        println("$owner onMethodEnter: hasMethodCost:$hasMethodCost")
        super.onMethodEnter()
        if (hasMethodCost) {
            labelBegin = Label()
            mv.visitLabel(labelBegin)
            mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J");
            time = newLocal(Type.LONG_TYPE)//声明临时变量time
            mv.visitVarInsn(LSTORE, time!!)//将返回的时间戳保存到临时变量
        }
    }

    override fun visitInsn(opcode: Int) {
        println("$owner visitInsn: opcode:$opcode")
        if (opcode >= IRETURN && opcode <= RETURN || opcode === ATHROW) {
            if (hasMethodCost) {
                mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J")
                mv.visitVarInsn(LLOAD, time!!) //加载time临时变量
                mv.visitInsn(LSUB) //与当前时间戳相减
                mv.visitVarInsn(LSTORE, time!!)
                labelEnd = Label()
                mv.visitLabel(labelEnd)
                //以下是将方法耗时打印出来 Log.i("当前类名","方法名:"+time)
                mv.visitLdcInsn(owner)
                mv.visitTypeInsn(NEW, "java/lang/StringBuilder")
                mv.visitInsn(DUP)
                mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false)
                mv.visitLdcInsn("$name:")
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;", false)
                mv.visitVarInsn(LLOAD, time!!)
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;", false)
                mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false)
                mv.visitMethodInsn(INVOKESTATIC, "android/util/Log", "i", "(Ljava/lang/String;Ljava/lang/String;)I", false)
                mv.visitInsn(POP)
                mv.visitLocalVariable("time", "J", null, labelBegin, labelEnd, time!!)
            }
        }
        super.visitInsn(opcode)
    }

    //即将从这个方法出去
    override fun onMethodExit(opcode: Int) {
        println("$owner onMethodExit: opcode:$opcode")
        super.onMethodExit(opcode)
    }

    override fun visitEnd() {
        println("$owner visitEnd:")
        super.visitEnd()
    }


}